package edu.uwm.cs552;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Observer;

import javax.swing.SwingUtilities;

/**
 * All the information about a game.
 */
public final class RailGame {

	public static Color[] playerColors = {
		Color.BLUE,
		Color.GREEN.darker(),
		Color.MAGENTA.darker(),
		Color.YELLOW.darker(),
		Color.DARK_GRAY,
	};
	
	public static final double STARTUP = 50;
	
	private final HexBoard board;
	private final Player[] players;
	
	/**
	 * Create a game using the given board with teh given number of players
	 * @param b board to use
	 * @param numPlayers number of players to allocate
	 */
	public RailGame(HexBoard b, int numPlayers) {
		if (b == null) {
			throw new IllegalArgumentException("Game requires a non-null board");
		}
		if (numPlayers <= 0 || numPlayers > playerColors.length) {
			throw new IllegalArgumentException("bad number of players: " + numPlayers);
		}
		board = b;
		players = new Player[numPlayers];
		for (int i=0; i < players.length; ++i) {
			players[i] = new Player(this,i+1,playerColors[i],STARTUP);
		}
	}
	
	// #(
	/**
	 * Get player-(i+1) in the game.
	 * @param i index of player, zero based
	 * @return player object
	 * @throws ArrayIndexOutOfBoundsException if index < 0 or greater than the number of players
	 */
	public Player getPlayer(int i) throws ArrayIndexOutOfBoundsException {
		return players[i];
	}

	/**
	 * Return terrain at this coordinate, or null if no terrain here
	 * @param h coordinate, must not be null
	 * @return terrain at this coordinate
	 */
	public Terrain getTerrain(HexCoordinate h) {
		return board.getTerrain(h);
	}
	
	/**
	 * Return barrier at this location on the board, or null if no barrier
	 * @param e edge, must not be null
	 * @return barrier at this edge, or null
	 */
	public Barrier getBarrier(HexEdge e) {
		return board.getBarrier(e);
	}
	
	/**
	 * get construction cost used in this game.
	 * @return construction cost to use in this game
	 */
	public ConstructionCost getConstructionCost() {
		return DefaultConstructionCost.getInstance();
	}
	
	/**
	 * Get track rental cost used in this game
	 * @return track rental cost
	 */
	public TrackRental getTrackRental() {
		return DefaultTrackRental.getInstance();
	}
	
	/**
	 * Return the player whose track is on this edge, or null if
	 * no track built on this edge yet.
	 * @param e edge in question, may not be null
	 * @return player with track on this edge, or null
	 */
	public Player occupier(HexEdge e) {
		for (Player p : players) {
			if (p.getTrack(e) != null) return p;
		}
		return null;
	}
	
	/**
	 * Return the player whose train is on this coordinate, or null if
	 * no train there.
	 * @param h coordinate in question, may not be null
	 * @return player with train on this edge, or null
	 */
	public Player occupier(HexCoordinate h) {
		for (Player p : players) {
			if (p.occupies(h)) return p;
		}
		return null;
	}
	
	private static final int MIN_TIME = 10;
	
	/**
	 * Check to see if over half of the players are waiting.
	 * If so, give everyone more time.
	 */
	public void checkTime() {
		int count = 0;
		int max = 0;
		for (Player p : players) {
			int t = p.getTime();
			if (t < MIN_TIME) {
				++count;
				if (t > max) max = t;
			}
		}
		if (count*2 > players.length) {
			for (Player p : players) {
				p.addTime(MIN_TIME-max);
			}
			// perhaps still a majority under the time:
			checkTime();
		}
	}

	// #)
	// TODO: Add methods so people can ask things about the board
	// or about players' activities on the board.  Also write
	// a method to top up time.
	

	/**
	 * Draw all players' actions according to a particular player's perspective.
	 * We assume the game board is already drawn.
	 * If the observer is null, then use the omniscient perspective, which sees
	 * everything.
	 * @param g graphics context to be used, must be a graphics2D object
	 * @param scale scale to draw board at
	 * @param observer perspective to take
	 */
	public void draw(Graphics g, double scale, Player observer) {
		// #(
		Graphics2D g2 = (Graphics2D)g;
		for (Player p : players) {
			p.draw(g2,scale,observer == null || p == observer);
		}
		// #)
		// TODO
	}
	
	// Fake being an observable
	
	public void addObserver(Observer arg0) {
		for (Player p : players) {
			p.addObserver(arg0);
		}
	}

	public void deleteObserver(Observer arg0) {
		for (Player p : players) {
			p.deleteObserver(arg0);
		}
	}

	public void deleteObservers() {
		for (Player p : players) {
			p.deleteObservers();
		}
	}

	public static void main(String[] args) {
		if (args.length != 2) {
			System.err.println("RailGame requires two arguments: game-board and number of players");
			System.exit(1);
		}
		final HexBoard board = new HexBoard();
		try {
			board.read(new BufferedReader(new FileReader(args[0])));
			final int numPlayers = Integer.parseInt(args[1]);
			final RailGame game = new RailGame(board,numPlayers);
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					PlayerFrame main = new PlayerFrame(game,null,board);
					main.setVisible(true);
					for (int i=0; i < numPlayers; ++i) {
						new PlayerFrame(game,game.getPlayer(i),board).setVisible(true);
					}
				}
			});
		} catch (Exception e) {
			System.err.println(e.getLocalizedMessage());
			System.exit(1);
		}
	}
}
